package com.example.demo.config;

import com.example.demo.common.Result;
import com.example.demo.common.constant.ResultCode;
import com.example.demo.common.constant.ThrowFlag;
import com.example.demo.exception.*;
import io.jsonwebtoken.SignatureException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolationException;

@ControllerAdvice
@ResponseBody
@Slf4j
public class ExceptionConfig {
    // Request参数缺失触发
    @ExceptionHandler(MissingServletRequestParameterException.class)
    public Result handler(MissingServletRequestParameterException ex) {
        Result res = new Result();
        res.setValidError("接口Request参数缺失", ResultCode.PARAM_DELETION);
        return res;
    }

    // Request单个参数类型不匹配触发
    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    public Result handler(MethodArgumentTypeMismatchException ex) {
        Result res = new Result();
        res.setValidError("接口Request传参类型不匹配", ResultCode.PARAM_TYPE_ERROR);
        return res;
    }

    // Request单个参数注解验证触发
    @ExceptionHandler(ConstraintViolationException.class)
    public Result handler(ConstraintViolationException ex) {
        Result res = new Result();
        String message = ex.getMessage().split(",")[0].trim();
        message = message.split(":")[1].trim();
        setErrorMessage(message, res);

        return res;
    }

    // DTO参数类型不匹配触发
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public Result handler(HttpMessageNotReadableException ex) {
        Result res = new Result();
        res.setValidError("接口DTO传参类型不匹配", ResultCode.PARAM_TYPE_ERROR);
        return res;
    }

    // DTO参数注解验证触发
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result handler(MethodArgumentNotValidException ex) {
        Result res = new Result();
        BindingResult bindingResult = ex.getBindingResult();
        if (bindingResult.hasErrors()) {
            FieldError fieldError = bindingResult.getFieldError();
            if (fieldError != null) {
                String message = fieldError.getDefaultMessage();
                if (message == null) {
                    message = "";
                }
                setErrorMessage(message, res);
            }
        }

        return res;
    }

    // JWT签名加密不匹配
    @ExceptionHandler(SignatureException.class)
    public Result handler(HttpServletRequest request, SignatureException ex) {
        log.error(request.getServletPath() + "," + ResultCode.JWT_CHECK_ERROR.getMessage());

        Result res = new Result();
        res.setValidError(ResultCode.JWT_CHECK_ERROR);
        return res;
    }

    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(RuntimeException.class)
    public Result handler(HttpServletRequest request, RuntimeException ex) {
        log.error(request.getServletPath() + "," + ex.getMessage());

        Result res = new Result();
        res.setSystemError(ex.getMessage());
        return res;
    }

    // 自定义异常类：警告异常
    @ExceptionHandler(WarnException.class)
    public Result handler(HttpServletRequest request, WarnException ex) {
        log.error(request.getServletPath() + "," + ex.getResultCode().getMessage());

        Result res = new Result();
        res.warn(ex.getResultCode());
        return res;
    }

    // 自定义异常类：参数校验异常
    @ExceptionHandler(ValidException.class)
    public Result handler(HttpServletRequest request, ValidException ex) {
        log.error(request.getServletPath() + "," + ex.getResultCode().getMessage());

        Result res = new Result();
        res.setValidError(ex.getResultCode());
        return res;
    }

    // 自定义异常类：身份验证异常
    @ExceptionHandler(VisitException.class)
    public Result handler(HttpServletRequest request, VisitException ex) {
        log.error(request.getServletPath() + "," + ex.getResultCode().getMessage());

        Result res = new Result();
        res.setVisitError(ex.getResultCode());
        return res;
    }

    // 自定义异常类：数据异常
    @ExceptionHandler(DataException.class)
    public Result handler(HttpServletRequest request, DataException ex) {
        Result res = new Result();
        String flag = ex.getFlag();

        if (flag.equals(ThrowFlag.CODE)) {
            log.error(request.getServletPath() + "," + ex.getResultCode().getMessage());
            res.setDataError(ex.getResultCode());
        }
        else if (flag.equals(ThrowFlag.MESSAGE_CODE)) {
            log.error(request.getServletPath() + "," + ex.getMessage());
            res.setDataError(ex.getMessage(), ex.getResultCode());
        }
        else {
            log.error(request.getServletPath() + "," + ex.getMessage());
            res.setDataError(ex.getMessage());
        }

        return res;
    }

    // 自定义异常类：业务逻辑异常
    @ExceptionHandler(BusinessException.class)
    public Result handler(HttpServletRequest request, BusinessException ex) {
        Result res = new Result();
        String flag = ex.getFlag();

        if (flag.equals(ThrowFlag.CODE)) {
            log.error(request.getServletPath() + "," + ex.getResultCode().getMessage());
            res.setBusinessError(ex.getResultCode());
        }
        else if (flag.equals(ThrowFlag.MESSAGE_CODE)) {
            log.error(request.getServletPath() + "," + ex.getMessage());
            res.setBusinessError(ex.getMessage(), ex.getResultCode());
        }
        else {
            log.error(request.getServletPath() + "," + ex.getMessage());
            res.setBusinessError(ex.getMessage());
        }

        return res;
    }

    // 其它异常触发（开启后，日志里没有出错代码的定位了，但不影响AOP的异常处理）
    @ExceptionHandler(Exception.class)
    public Result handler(HttpServletRequest request, Exception ex) {
        log.error(request.getServletPath() + "," + ResultCode.SYSTEM_OTHER_ERROR.getMessage() + "," + ex.toString());

        Result res = new Result();
        res.setSystemError(ResultCode.SYSTEM_OTHER_ERROR);
        return res;
    }

    private void setErrorMessage(String message, Result res) {
        if (message.contains("文件")) {
            if (message.contains("为空")) {
                res.setValidError(message, ResultCode.FILE_IS_NULL);
            }
            else if (message.contains("格式")) {
                res.setValidError(message, ResultCode.FILE_FORMAT_ERROR);
            }
            else if (message.contains("转换")) {
                res.setValidError(message, ResultCode.FILE_CONVERT_ERROR);
            }
            else if (message.contains("超出")) {
                res.setValidError(message, ResultCode.FILE_SIZE_EXCEED);
            }
            else {
                res.setValidError(message);
            }
        }
        else {
            if (message.contains("为null")) {
                res.setValidError(message, ResultCode.PARAM_IS_NULL);
            }
            else if (message.contains("为空")) {
                res.setValidError(message, ResultCode.PARAM_IS_BLANK);
            }
            else if (message.contains("格式")) {
                res.setValidError(message, ResultCode.PARAM_FORMAT_ERROR);
            }
            else if (message.contains("特殊字符")) {
                res.setValidError(message, ResultCode.PARAM_SPECIAL_CHAR);
            }
            else if (message.contains("转换")) {
                res.setValidError(message, ResultCode.PARAM_CONVERT_ERROR);
            }
            else if (message.contains("XSS")) {
                res.setValidError(message, ResultCode.PARAM_XSS_ATTACK);
            }
            else if (message.contains("超出范围")) {
                res.setValidError(message, ResultCode.PARAM_RANGE_ERROR);
            }
            else {
                res.setValidError(message);
            }
        }
    }
}
